#pragma once

namespace SD_Tools
{
	class DirectXHelper
	{
	private:
	public:
	private:
	public:
		static HRESULT GenerateShader(IDirect3DDevice9* Device, IDirect3DPixelShader9** Shader, float r, float g, float b, bool SetZBuffer);
		static HRESULT GenerateShader_OLD(IDirect3DDevice9* Device, IDirect3DPixelShader9** OutShader, float r, float g, float b);
		static HRESULT GenerateSimpleShader(IDirect3DDevice9* Device, IDirect3DPixelShader9** OutShader);
		static HRESULT GenerateTexture(IDirect3DDevice9* Device, IDirect3DTexture9** Texture, DWORD Color);
		static bool GenerateTextureCRC32(IDirect3DDevice9* Device, IDirect3DTexture9* Texture, DWORD* Out);
		static void CreateShaderConstantFBy32(DWORD CRC, Vector4* Out);
	};

	//************************************
	// Method:    GenerateShader
	// Profit:	  Generates a PixelShader in the given color
	// FullName:  SD_Tools::DirectXHelper::GenerateShader
	// Returns:   HRESULT
	// Notes:
	// -Creates a single color shader version 3.0
	// -Should work with NVidia and Asus graphic-cards
	//************************************
	HRESULT DirectXHelper::GenerateShader( IDirect3DDevice9* Device, IDirect3DPixelShader9** Shader, float r, float g, float b, bool SetZBuffer )
	{
		char ShaderAsmBuf[256]; 
		ID3DXBuffer* ShaderBuf = NULL;
		if (SetZBuffer)
		{
			sprintf_s(ShaderAsmBuf, "ps_3_0\ndef c0, %f, %f, %f, %f\nmov oC0,c0\nmov oDepth, c0.x", r, g, b, 1.0f); 
		}
		else
		{
			sprintf_s(ShaderAsmBuf, "ps_3_0\ndef c0, %f, %f, %f, %f\nmov oC0,c0", r, g, b, 1.0f);
		}
		if(FAILED(D3DXAssembleShader(ShaderAsmBuf, (strlen(ShaderAsmBuf)+1), NULL, NULL, 0, &ShaderBuf, NULL)))
		{
			return E_FAIL;
		}
		return Device->CreatePixelShader((const DWORD*)ShaderBuf->GetBufferPointer(), Shader);
	}

	//************************************
	// Method:    GenerateShader_OLD
	// Profit:	  Creates a PixelShader in the given color
	// FullName:  SD_Tools::DirectXHelper::GenerateShader_OLD
	// Returns:   HRESULT
	// Notes:
	// -Creates a single color shader version 1.1
	// -Wont work with some Asus graphic-cards
	//************************************
	HRESULT DirectXHelper::GenerateShader_OLD( IDirect3DDevice9* Device, IDirect3DPixelShader9** OutShader, float r, float g, float b )
	{
		char szShader[64];
		ID3DXBuffer* pBuffer = NULL;
		sprintf(szShader, "ps.1.1\ndef c0, %f, %f, %f, %f\nmov r0,c0", r, g, b, 1.0f);
		if(FAILED(D3DXAssembleShader(szShader, sizeof(szShader), NULL, NULL, 0, &pBuffer, NULL)))
		{
			return E_FAIL;
		}
		return Device->CreatePixelShader((const DWORD*)pBuffer->GetBufferPointer(), OutShader);
	}

	//************************************
	// Method:    GenerateSimpleShader
	// Profit:	  Creates a simple empty shader
	// FullName:  SD_Tools::DirectXHelper::GenerateSimpleShader
	// Returns:   HRESULT
	// Notes:
	// -Creates a shader version 1.3
	// -Wont work with some Asus graphic-cards
	//************************************
	HRESULT DirectXHelper::GenerateSimpleShader( IDirect3DDevice9* Device, IDirect3DPixelShader9** OutShader )
	{
		HRESULT temp;
		char szShader[] = "ps_1_3 \nmov r0, c0 \n";
		ID3DXBuffer* pBuffer = NULL;

		if( FAILED(D3DXAssembleShader(szShader, sizeof(szShader), NULL, NULL, 0, &pBuffer, NULL)) )
		{
			return E_FAIL;
		}
		temp = Device->CreatePixelShader((const DWORD*)pBuffer->GetBufferPointer(), OutShader);
		pBuffer->Release();
		return temp;
	}

	//************************************
	// Method:    GenerateTexture
	// Profit:	  Creates a Texture
	// FullName:  SD_Tools::DirectXHelper::GenerateTexture
	// Returns:   HRESULT
	// Notes:
	// -Creates a single color Texture in 8*8 size
	//************************************
	HRESULT DirectXHelper::GenerateTexture(IDirect3DDevice9* Device, IDirect3DTexture9** Texture, DWORD Color)
	{
		if( FAILED(Device->CreateTexture(8, 8, 1, 0, D3DFMT_A4R4G4B4, D3DPOOL_MANAGED, Texture, NULL)) )
		{
			return E_FAIL;
		}
		WORD Color16 = ((WORD)((Color>>28)&0xF)<<12) | (WORD)(((Color>>20)&0xF)<<8) | (WORD)(((Color>>12)&0xF)<<4) | (WORD)(((Color>>4)&0xF)<<0);
		D3DLOCKED_RECT d3dlr;
		(*Texture)->LockRect(0, &d3dlr, 0, 0);
		WORD *pDst16 = (WORD*)d3dlr.pBits;
		for(int xy=0; xy < 8*8; xy++)
		{
			*pDst16++ = Color16;
		}
		(*Texture)->UnlockRect(0);
		return S_OK;
	}

	//************************************
	// Method:    GenerateTextureCRC32
	// Profit:	  Generates a CRC of any given Texture!
	// FullName:  SD_Tools::DirectXHelper::GenerateTextureCRC32
	// Returns:   bool false upon failure
	// Notes:
	// -Might be VERY slow - depends on the type of the texture
	// -Should work on any texture
	// -Use a SimpleTree or a std::map for managing textures - See TexManager.h for an idea.
	//************************************
	bool DirectXHelper::GenerateTextureCRC32( IDirect3DDevice9* Device, IDirect3DTexture9* Texture, DWORD* Out )
	{
		static bool InTexCRC = false;
		bool Rslt = false;
		*Out = 0;
		if (InTexCRC)
		{
			return false;
		}
		InTexCRC = true;
		if (Texture)
		{
			D3DLOCKED_RECT MyLockedRect;
			if (Texture->LockRect(0, &MyLockedRect, NULL, D3DLOCK_READONLY) == D3D_OK)
			{
				if (MyLockedRect.Pitch)
				{
					Rslt = true;
					*Out = SD_Tools::General::SDHash32(MyLockedRect.pBits, MyLockedRect.Pitch);
				}
				Texture->UnlockRect(0);
			}
			else
			{
				IDirect3DSurface9* VideoSurface;
				IDirect3DSurface9* RenderSurface;
				IDirect3DSurface9* OldSurface;
				IDirect3DBaseTexture9* OldTexture;
				D3DSURFACE_DESC VideoSurfaceDesc;

				if (Texture->GetLevelDesc(0, &VideoSurfaceDesc) == D3D_OK)
				{
					if (VideoSurfaceDesc.Pool == D3DPOOL_DEFAULT)
					{
						if(Texture->GetSurfaceLevel(0, &VideoSurface) == D3D_OK)
						{
							if (VideoSurface->GetDesc(&VideoSurfaceDesc) == D3D_OK)
							{
								switch (VideoSurfaceDesc.Format)
								{
								case D3DFMT_DXT1:
								case D3DFMT_DXT2:
								case D3DFMT_DXT3:
								case D3DFMT_DXT4:
								case D3DFMT_DXT5:
									{
										if (Device->CreateRenderTarget(VideoSurfaceDesc.Width, VideoSurfaceDesc.Height, D3DFMT_R5G6B5, D3DMULTISAMPLE_NONE, 0, true, &RenderSurface, NULL) == D3D_OK) //D3DFMT_A8R8G8B8 VideoSurfaceDesc.Format
										{
											if (Device->GetRenderTarget(0, &OldSurface) == D3D_OK)
											{
												if (Device->SetRenderTarget(0, RenderSurface) == D3D_OK)
												{
													AbstractDraw::ClearRenderTarget(Device);
													if (Device->GetTexture(0, &OldTexture) == D3D_OK)
													{
														IDirect3DStateBlock9* TempStateBlock;

														if (AbstractDraw::PrepareStateBlock(Device, &TempStateBlock) == D3D_OK)
														{
															TEXVERTEX* TexVertices = AbstractDraw::AcquireTextureBuffer();

															AbstractDraw::CreateTextureRectangle(TexVertices, Rectangle2(0, 0, VideoSurfaceDesc.Width, VideoSurfaceDesc.Height));

															AbstractDraw::BeginStateBlock(Device, TempStateBlock, TexFVF);

															if (Device->SetTexture(0, Texture) == D3D_OK)
															{
																AbstractDraw::DrawTextureBuffer(Device, 6, TexVertices);

																if (RenderSurface->LockRect(&MyLockedRect, NULL, D3DLOCK_READONLY) == D3D_OK)
																{
																	if (MyLockedRect.Pitch)
																	{
																		Rslt = true;
																		*Out = SD_Tools::General::SDHash32(MyLockedRect.pBits, MyLockedRect.Pitch*VideoSurfaceDesc.Height);
																	}
																	RenderSurface->UnlockRect();
																}

																Device->SetTexture(0, OldTexture);
															}
															AbstractDraw::EndStateBlock(TempStateBlock);
															TempStateBlock->Release();
															AbstractDraw::ReleaseTextureBuffer(TexVertices);
														}
														OldTexture->Release();
													}
													Device->SetRenderTarget(0, OldSurface);
												}
												OldSurface->Release();
											}
											RenderSurface->Release();
										}
									}
									break;
								default:
									{
										if (Device->CreateRenderTarget(VideoSurfaceDesc.Width, VideoSurfaceDesc.Height, VideoSurfaceDesc.Format, D3DMULTISAMPLE_NONE, 0, true, &RenderSurface, NULL) == D3D_OK) //D3DFMT_A8R8G8B8 VideoSurfaceDesc.Format
										{
											if (Device->StretchRect(VideoSurface, NULL, RenderSurface, NULL, D3DTEXF_NONE) == D3D_OK)
											{
												if (RenderSurface->LockRect(&MyLockedRect, NULL, D3DLOCK_READONLY) == D3D_OK)
												{
													if (MyLockedRect.Pitch)
													{
														Rslt = true;
														*Out = SD_Tools::General::SDHash32(MyLockedRect.pBits, MyLockedRect.Pitch*VideoSurfaceDesc.Height);
													}
													RenderSurface->UnlockRect();
												}
											}
											RenderSurface->Release();
										}
									}
									break;
								}
							}
							VideoSurface->Release();
						}
					}
				}
			}
		}
		InTexCRC = false;
		return Rslt;
	}

	//************************************
	// Method:    CreateShaderConstantFBy32
	// Profit:	  Creates a Vector4 Colorfloatarray from a 32 bit CRC
	// FullName:  SD_Tools::DirectXHelper::CreateShaderConstantFBy32
	// Returns:   void
	// Notes:
	// -The lowest byte of the DWORD is ignored
	// -Out->w is == 1.0f
	//************************************
	void DirectXHelper::CreateShaderConstantFBy32( DWORD CRC, Vector4* Out )
	{
		Out->x = ((CRC >> 8) & 0xFF) / 255.0f;
		Out->y = ((CRC >> 16) & 0xFF) / 255.0f;
		Out->z = ((CRC >> 24) & 0xFF) / 255.0f;
		Out->w = 1.0f;
	}

};






















